﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Kenly.DBFramework.Reflection;
using Kenly.DBFramework;
using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    /// The gateway to a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class OleTableGatewayUtility<T> : TableGatewayUtilityBase<T> where T : TableGateway<T>, new()
    {

        public OleTableGatewayUtility(DatabaseType databaseType, string connectionString)
            : base(databaseType, connectionString)
        {
        }

        private static object GetNextIdentity(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector)
        {
            string commandText = commandReflector.SelectNextIdentity(typeof(T));
            return tableCommandExecutor.ExecuteScalar(commandText, false);
        }

        private static void UpdateIdentity(T entity, object nextIdentity)
        {
            IdentityUtility.SetIdentityValue(entity, nextIdentity);
        }

        public override int AddNew(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector, T entity, bool returnIdentity)
        {
            if (entity == null)
            {
                return 0;
            }

            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }
            List<IDataParameter> paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
            
            string insertStatement = commandReflector.Insert(typeof(T), false, propertyNames);
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras);
            if (affectedRowCount > 0 && returnIdentity)
            {
                entity.ClearChangedPropertyNames();
                object nextIdentity = GetNextIdentity(tableCommandExecutor, commandReflector);
                UpdateIdentity(entity, nextIdentity);
            }
            return affectedRowCount;
        }

        protected override int AddNew(T entity, bool returnIdentity, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entity == null)
            {
                return 0;
            }

            //returnIdentity = false;

            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }
            List<IDataParameter> paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
            //if (returnIdentity)
            //{
            //    IDataParameter idParameter = ParameterReflection.GetIdentityParameter(base.DatabaseType, entity, returnIdentity);
            //    paras.Add(idParameter);
            //}

            ICommandReflector commandReflector = CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);
            string insertStatement = commandReflector.Insert(typeof(T), false, propertyNames);

            if (!commitTransaction)
            {
                tableCommandExecutor.BeginTransaction();
            }
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras);
            if (affectedRowCount > 0 && returnIdentity)
            {
                entity.ClearChangedPropertyNames();
                object nextIdentity = GetNextIdentity(tableCommandExecutor, commandReflector);
                UpdateIdentity(entity, nextIdentity);
            }
            return affectedRowCount;
        }

        protected override int AddNew(IList<T> entities, bool returnIdentity, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entities == null || entities.Count <= 0)
            {
                return 0;
            }

            //returnIdentity = false;

            string insertStatement = "";
            List<IDataParameter> paras = null;
            List<string> propertyNames;
            int affectedRowCount = 0;

            ICommandReflector commandReflector = CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);

            try
            {
                tableCommandExecutor.BeginTransaction();
                foreach (T entity in entities)
                {
                    propertyNames = entity.ChangedPropertyNames;
                    if (propertyNames == null || propertyNames.Count <= 0)
                    {
                        continue;
                    }
                    paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
                    //if (returnIdentity)
                    //{
                    //    IDataParameter idParameter = ParameterReflection.GetIdentityParameter(base.DatabaseType, entity, returnIdentity);
                    //    paras.Add(idParameter);
                    //}
                    insertStatement = commandReflector.Insert(typeof(T), false, propertyNames);
                    if (tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras) > 0)
                    {
                        affectedRowCount++;
                        if (returnIdentity)
                        {
                            object nextIdentity = GetNextIdentity(tableCommandExecutor, commandReflector);
                            UpdateIdentity(entity, nextIdentity);
                        }
                        entity.ClearChangedPropertyNames();
                    }
                }
                if (commitTransaction)
                {
                    tableCommandExecutor.Commit();
                }
                return affectedRowCount;
            }
            catch (Exception ex)
            {
                tableCommandExecutor.Rollback();
                throw new CommandException(insertStatement,MsgResource.ExecuteNonQueryFailed, ex);
            }
        }
    }


}